/****************************************************************************************************
 * 项目：词法分析
 * 描述：。
 ****************************************************************************************************/
#include "zhi.h"
struct 关键词S {
   char*       关键字名字;/*关键字名字*/
   uint8_t     长度;/*关键字长度*/
   单词类型E   token;/*token是token类型*/
};  //系统关键字(保留字)结构

//关键字查找表
struct 关键词S 关键字表[] = {
   {"var",	  3,	TOKEN_VAR}, 
   {"fun",	  3,	TOKEN_FUN}, 
   {"if",	  2,	TOKEN_IF}, 
   {"else",	  4,  	TOKEN_ELSE},
   {"true",	  4,  	TOKEN_TRUE}, 
   {"false",	  5,  	TOKEN_FALSE}, 
   {"while",	  5,  	TOKEN_WHILE}, 
   {"for",	  3,  	TOKEN_FOR}, 
   {"break",	  5,  	TOKEN_BREAK}, 
   {"continue",   8,    TOKEN_CONTINUE},
   {"return",	  6,  	TOKEN_RETURN}, 
   {"null",	  4,  	TOKEN_NULL}, 
   {"class",	  5,  	TOKEN_CLASS},
   {"is",	  2,  	TOKEN_IS},
   {"static",	  6,  	TOKEN_STATIC},
   {"this",	  4,  	TOKEN_THIS},
   {"super",	  5,  	TOKEN_SUPER},
   {"import",	  6,  	TOKEN_IMPORT},
   {NULL,	  0,  	TOKEN_UNKNOWN}
};

// 判断start是否为关键字并返回相应的token
/*判断以start为起始，长度为length的单词是否为关键字，返回相应token类型。如果关键词数组中没有查找到，说明是普通的标识符，则返回TOKEN_ID*/
static 单词类型E 是普通标识符或关键字(const char* start, uint32_t 长度) {
   uint32_t idx = 0;
   while (关键字表[idx].关键字名字 != NULL) {
      if (关键字表[idx].长度 == 长度 && \
	    memcmp(关键字表[idx].关键字名字, start, 长度) == 0) {
	 return 关键字表[idx].token;
      }
      idx++;
   }
   return TOKEN_ID;
}

// 向前看一个字符
char 向前看一个字符(词法分析器* 词法分析器S) {
   return *词法分析器S->源码字符串的下一个字符;
}

//获取下一字符
static void 取下一个字符(词法分析器* 词法分析器S) {
   词法分析器S->当前字符 = *词法分析器S->源码字符串的下一个字符++;
}

//查看下一个字符是否为期望的,如果是就读进来,返回true,否则返回false
static bool 匹配下一个字符(词法分析器* 词法分析器S, char expectedChar) {
   if (向前看一个字符(词法分析器S) == expectedChar) {
      取下一个字符(词法分析器S);
      return true;
   }
   return false;
}

static void 跳过连续的空白符(词法分析器* 词法分析器S) {
   while (isspace(词法分析器S->当前字符)) {
      if (词法分析器S->当前字符 == '\n') {
	 词法分析器S->当前单词.行号++;
      }
      取下一个字符(词法分析器S);
   }
}

//解析标识符。变量名，函数名都成为标识符。
static void 解析标识符(词法分析器* 词法分析器S, 单词类型E type) {
   while (isalnum(词法分析器S->当前字符) || 词法分析器S->当前字符 == '_') {
      取下一个字符(词法分析器S);
   }

   //nextCharPtr会指向第1个不合法字符的下一个字符,因此-1
   uint32_t 长度 = (uint32_t)(词法分析器S->源码字符串的下一个字符 - 词法分析器S->当前单词.start - 1);
   if (type != TOKEN_UNKNOWN) {
      词法分析器S->当前单词.type = type; 
   } else {
      词法分析器S->当前单词.type = 是普通标识符或关键字(词法分析器S->当前单词.start, 长度);
   }
   词法分析器S->当前单词.长度 = 长度;
}

static void 解析十六进制数字(词法分析器* 词法分析器S) {
   while (isxdigit(词法分析器S->当前字符)) {
      取下一个字符(词法分析器S);
   }
}

static void 解析十进制数字(词法分析器* 词法分析器S) {
   while (isdigit(词法分析器S->当前字符)) {
      取下一个字符(词法分析器S);
   }

   //若有小数点
   if (词法分析器S->当前字符 == '.' && isdigit(向前看一个字符(词法分析器S))) {
      取下一个字符(词法分析器S);
      while (isdigit(词法分析器S->当前字符)) { //解析小数点之后的数字
	 取下一个字符(词法分析器S);
      }
   }
}

//解析八进制
static void 解析八进制数字(词法分析器* 词法分析器S) {
   while(词法分析器S->当前字符 >= '0' && 词法分析器S->当前字符 < '8') {
      取下一个字符(词法分析器S);
   } 
}

//解析八进制 十进制 十六进制 仅支持前缀形式,后缀形式不支持
static void 解析数字(词法分析器* 词法分析器S) {
   //十六进制的0x前缀
   if (词法分析器S->当前字符 == '0' && 匹配下一个字符(词法分析器S, 'x')) {
      取下一个字符(词法分析器S);  //跳过'x'
      解析十六进制数字(词法分析器S);   //解析十六进制数字
      词法分析器S->当前单词.value = 
	 NUM_TO_VALUE(strtol(词法分析器S->当前单词.start, NULL, 16));
   } else if (词法分析器S->当前字符 == '0' 
	 && isdigit(向前看一个字符(词法分析器S))) {  // 八进制
      解析八进制数字(词法分析器S);
      词法分析器S->当前单词.value = 
	 NUM_TO_VALUE(strtol(词法分析器S->当前单词.start, NULL, 8));
   } else {	  //解析十进制
      解析十进制数字(词法分析器S);
      词法分析器S->当前单词.value = NUM_TO_VALUE(strtod(词法分析器S->当前单词.start, NULL));
   }
   //nextCharPtr会指向第1个不合法字符的下一个字符,因此-1
   词法分析器S->当前单词.长度 = 
      (uint32_t)(词法分析器S->源码字符串的下一个字符 - 词法分析器S->当前单词.start - 1);
   词法分析器S->当前单词.type = TOKEN_NUM;
}

/*解析unicode码点。将unicode码点按照utf-8编码后写入buf指定缓冲区中。*/
static void 将unicode码点按照UTF8编码后写入指定缓冲区中(词法分析器* 词法分析器S, ByteBuffer* buf) {
   uint32_t idx = 0;
   int value = 0;
   uint8_t digit = 0;

//获取数值,u后面跟着4位十六进制数字
   while(idx++ < 4) {
      取下一个字符(词法分析器S);
      if (词法分析器S->当前字符 == '\0') {
	 词法分析_错误(词法分析器S, "unterminated unicode!");
      }
      if (词法分析器S->当前字符 >= '0' && 词法分析器S->当前字符 <= '9') {
	 digit = 词法分析器S->当前字符 - '0';
      } else if (词法分析器S->当前字符 >= 'a' && 词法分析器S->当前字符 <= 'f') {
	 digit = 词法分析器S->当前字符 - 'a' + 10;
      } else if (词法分析器S->当前字符 >= 'A' && 词法分析器S->当前字符 <= 'F') {
	 digit = 词法分析器S->当前字符 - 'A' + 10;
      } else {
	 词法分析_错误(词法分析器S, "invalid unicode!");
      }
      value = value * 16 | digit;
   }

   uint32_t byteNum = 按照UTF8编码的字节数(value);
   ASSERT(byteNum != 0, "utf8 encode bytes should be between 1 and 4!");

   //为代码通用, 下面会直接写buf->数据缓冲区,在此先写入byteNum个0,以保证事先有byteNum个空间
   ByteBufferFillWrite(词法分析器S->虚拟机S, buf, 0, byteNum);

   //把value编码为utf8后写入缓冲区buf
   按照UTF8编码写入缓冲区(buf->数据缓冲区 + buf->count - byteNum, value);
}

//解析字符串
static void 解析字符串(词法分析器* 词法分析器S) {
   ByteBuffer str;
   ByteBufferInit(&str);
   while (true) {
      取下一个字符(词法分析器S);

      if (词法分析器S->当前字符 == '\0') {
	 词法分析_错误(词法分析器S, "unterminated string!");
      }

      if (词法分析器S->当前字符 == '"') {
	 词法分析器S->当前单词.type = TOKEN_STRING;
	 break;
      }

      if (词法分析器S->当前字符 == '%') {
	 if (!匹配下一个字符(词法分析器S, '(')) {
	    词法分析_错误(词法分析器S, "'%' should followed by '('!");
	 }
	 if (词法分析器S->应该插入的右括号数 > 0) {
	    编译_错误(词法分析器S, "sorry, I don`t support nest interpolate 表达式!");
	 }
	 词法分析器S->应该插入的右括号数 = 1;
	 词法分析器S->当前单词.type = TOKEN_INTERPOLATION;
	 break;
      }

      if (词法分析器S->当前字符 == '\\') {   //处理转义字符
	 取下一个字符(词法分析器S);
         switch (词法分析器S->当前字符) {
            case '0': 
               ByteBufferAdd(词法分析器S->虚拟机S, &str, '\0'); 
               break;
            case 'a': 
               ByteBufferAdd(词法分析器S->虚拟机S, &str, '\a'); 
               break;
            case 'b': 
               ByteBufferAdd(词法分析器S->虚拟机S, &str, '\b');
               break;
            case 'f':
               ByteBufferAdd(词法分析器S->虚拟机S, &str, '\f'); 
               break;
            case 'n': 
               ByteBufferAdd(词法分析器S->虚拟机S, &str, '\n'); 
               break;
            case 'r': 
               ByteBufferAdd(词法分析器S->虚拟机S, &str, '\r'); 
               break;
            case 't': 
               ByteBufferAdd(词法分析器S->虚拟机S, &str, '\t'); 
               break;
            case 'u': 
               将unicode码点按照UTF8编码后写入指定缓冲区中(词法分析器S, &str); 
               break;
            case '"': 
               ByteBufferAdd(词法分析器S->虚拟机S, &str, '"'); 
               break;
            case '\\': 
               ByteBufferAdd(词法分析器S->虚拟机S, &str, '\\'); 
               break;
            default:
	       词法分析_错误(词法分析器S, "unsupport escape \\%c", 词法分析器S->当前字符);
               break;
         }
      } else {   //普通字符
	 ByteBufferAdd(词法分析器S->虚拟机S, &str, 词法分析器S->当前字符); 
      }
   }

   //用识别到的字符串新建字符串对象存储到curToken的value中
   字符串对象S* objZiFuChuan = 以字符串创建字符串对象(词法分析器S->虚拟机S, (const char*)str.数据缓冲区, str.count);
  // 词法分析器S->当前单词.value = OBJ_TO_VALUE(objZiFuChuan);

   SET_VALUE_OBJ(&词法分析器S->当前单词.value, objZiFuChuan);
   ByteBufferClear(词法分析器S->虚拟机S, &str);
}

// 跳过一行
static void 跳过一行(词法分析器* 词法分析器S) {
   取下一个字符(词法分析器S);
   while (词法分析器S->当前字符 != '\0') {
      if (词法分析器S->当前字符 == '\n') {
	 词法分析器S->当前单词.行号++;
	 取下一个字符(词法分析器S);
	 break;
      }
      取下一个字符(词法分析器S);
   }
}

//跳过行注释或区块注释
static void 跳过注释(词法分析器* 词法分析器S) {
   char nextChar = 向前看一个字符(词法分析器S);
   if (词法分析器S->当前字符 == '/') {  // 行注释
      跳过一行(词法分析器S);
   } else {   // 区块注释
      while (nextChar != '*' && nextChar != '\0') {
	 取下一个字符(词法分析器S);
	 if (词法分析器S->当前字符 == '\n') {
	    词法分析器S->当前单词.行号++;
	 }
	 nextChar = 向前看一个字符(词法分析器S);
      }
      if (匹配下一个字符(词法分析器S, '*')) {
	 if (!匹配下一个字符(词法分析器S, '/')) {   //匹配*/
	    词法分析_错误(词法分析器S, "expect '/' after '*'!");
	 }
	 取下一个字符(词法分析器S);
      } else {
	 词法分析_错误(词法分析器S, "应该在文件结束前加上'*/'!");
      }
   }
   跳过连续的空白符(词法分析器S);  //注释之后有可能会有空白字符
}

//获得下一个token
void o_取下个单词(词法分析器* 词法分析器S) {
   词法分析器S->前一个单词 = 词法分析器S->当前单词;
   跳过连续的空白符(词法分析器S);  // 跳过待识别单词之前的空格
   词法分析器S->当前单词.type = TOKEN_EOF;
   词法分析器S->当前单词.长度 = 0;
   词法分析器S->当前单词.start = 词法分析器S->源码字符串的下一个字符 - 1;
   词法分析器S->当前单词.value = VT_TO_VALUE(值类型_未定义);
   while (词法分析器S->当前字符 != '\0') { 
      switch (词法分析器S->当前字符) {
	 case ',':
	    词法分析器S->当前单词.type = TOKEN_COMMA;
	    break;
	 case ':':
	    词法分析器S->当前单词.type = TOKEN_COLON;
	    break;
	 case '(':
	    if (词法分析器S->应该插入的右括号数 > 0) {
	       词法分析器S->应该插入的右括号数++;
	    }
	    词法分析器S->当前单词.type = TOKEN_LEFT_PAREN;
	    break;
	 case ')':
	    if (词法分析器S->应该插入的右括号数 > 0) {
	       词法分析器S->应该插入的右括号数--;
	       if (词法分析器S->应该插入的右括号数 == 0) {
		  解析字符串(词法分析器S);
		  break;
	       }
	    }
	    词法分析器S->当前单词.type = TOKEN_RIGHT_PAREN;
	    break;
	 case '[':
	    词法分析器S->当前单词.type = TOKEN_LEFT_BRACKET;
	    break;
	 case ']':
	    词法分析器S->当前单词.type = TOKEN_RIGHT_BRACKET;
	    break;
	 case '{':
	    词法分析器S->当前单词.type = TOKEN_LEFT_BRACE;
	    break;
	 case '}':
	    词法分析器S->当前单词.type = TOKEN_RIGHT_BRACE;
	    break;
	 case '.':
	    if (匹配下一个字符(词法分析器S, '.')) {
	       词法分析器S->当前单词.type = TOKEN_DOT_DOT;
	    } else {
	       词法分析器S->当前单词.type = TOKEN_DOT;
	    }
	    break;
	 case '=':
	    if (匹配下一个字符(词法分析器S, '=')) {
	       词法分析器S->当前单词.type = TOKEN_EQUAL;
	    } else {
	       词法分析器S->当前单词.type = TOKEN_ASSIGN;
	    }
	    break;
	 case '+':
	    词法分析器S->当前单词.type = TOKEN_ADD;
	    break;
	 case '-':
	    词法分析器S->当前单词.type = TOKEN_SUB;
	    break;
	 case '*':
	    词法分析器S->当前单词.type = TOKEN_MUL;
	    break;
	 case '/':
	    //跳过注释'//'或'/*'
	    if (匹配下一个字符(词法分析器S, '/') || 匹配下一个字符(词法分析器S, '*')) {   //跳过注释'//'或'/*'
	       跳过注释(词法分析器S);

	       //重置下一个token起始地址
	       词法分析器S->当前单词.start = 词法分析器S->源码字符串的下一个字符 - 1;
	       continue;
	    } else {		 // '/'
	       词法分析器S->当前单词.type = TOKEN_DIV;
	    }
	    break;
	 case '%':
	    词法分析器S->当前单词.type = TOKEN_MOD;
	    break;
	 case '&':
	    if (匹配下一个字符(词法分析器S, '&')) {
	       词法分析器S->当前单词.type = TOKEN_LOGIC_AND;
	    } else {
	       词法分析器S->当前单词.type = TOKEN_BIT_AND;
	    }
	    break;
	 case '|':
	    if (匹配下一个字符(词法分析器S, '|')) {
	       词法分析器S->当前单词.type = TOKEN_LOGIC_OR;
	    } else {
	       词法分析器S->当前单词.type = TOKEN_BIT_OR;
	    }
	    break;
	 case '~':
	    词法分析器S->当前单词.type = TOKEN_BIT_NOT;
	    break;
	 case '?':
	    词法分析器S->当前单词.type = TOKEN_QUESTION;
	    break;
	 case '>':
	    if (匹配下一个字符(词法分析器S, '=')) {
	       词法分析器S->当前单词.type = TOKEN_GREATE_EQUAL;
	    } else if (匹配下一个字符(词法分析器S, '>')) {
	       词法分析器S->当前单词.type = TOKEN_BIT_SHIFT_RIGHT;
	    } else {
	       词法分析器S->当前单词.type = TOKEN_GREATE;
	    }
	    break;
	 case '<':
	    if (匹配下一个字符(词法分析器S, '=')) {
	       词法分析器S->当前单词.type = TOKEN_LESS_EQUAL;
	    } else if (匹配下一个字符(词法分析器S, '<')) {
	       词法分析器S->当前单词.type = TOKEN_BIT_SHIFT_LEFT;
	    } else {
	       词法分析器S->当前单词.type = TOKEN_LESS;
	    }
	    break;
	 case '!':
	    if (匹配下一个字符(词法分析器S, '=')) {
	       词法分析器S->当前单词.type = TOKEN_NOT_EQUAL;
	    } else {
	       词法分析器S->当前单词.type = TOKEN_LOGIC_NOT;
	    }
	    break;

	 case '"':
	    解析字符串(词法分析器S);
	    break;

	 default:    
	    //处理变量名及数字
	    //进入此分支的字符肯定是数字或变量名的首字符
	    //后面会调用相应函数把其余字符一并解析

	    //首字符是字母或'_'则是变量名
	    if (isalpha(词法分析器S->当前字符) || 词法分析器S->当前字符 == '_') {
	       解析标识符(词法分析器S, TOKEN_UNKNOWN);  //解析变量名其余的部分
	    } else if (isdigit(词法分析器S->当前字符)) { //数字
	       解析数字(词法分析器S);
	    } else {
	       if (词法分析器S->当前字符 == '#' && 匹配下一个字符(词法分析器S, '!')) {
		  跳过一行(词法分析器S);
		  词法分析器S->当前单词.start = 词法分析器S->源码字符串的下一个字符 - 1;  //重置下一个token起始地址
		  continue;
	       } 
	       词法分析_错误(词法分析器S, "unsupport char: \'%c\', quit.", 词法分析器S->当前字符);
	    }
	    return;
      }
      //大部分case的出口
      词法分析器S->当前单词.长度 = (uint32_t)(词法分析器S->源码字符串的下一个字符 - 词法分析器S->当前单词.start);
      取下一个字符(词法分析器S);
      return;
   }
}

//若当前token为expected则读入下一个token并返回true,
//否则不读入token且返回false
/*匹配当前token是否为参数expected,如果是就调用getNextToken()读入下一个token并返回true，否则返回false*/
bool 匹配单词(词法分析器* 词法分析器S, 单词类型E expected) {
   if (词法分析器S->当前单词.type == expected) {
      o_取下个单词(词法分析器S);
      return true;
   }
   return false;
}

//断言当前token为expected并读入下一token,否则报错errMsg
void 断言当前单词为expected(词法分析器* 词法分析器S, 单词类型E expected, const char* 错误信息) {
   if (词法分析器S->当前单词.type != expected) {
      编译_错误(词法分析器S, 错误信息);
   }
   o_取下个单词(词法分析器S);
}

//断言下一个token为expected,否则报错errMsg
void 断言下一个单词为expected(词法分析器* 词法分析器S, 单词类型E expected, const char* 错误信息) {
   o_取下个单词(词法分析器S);
   if (词法分析器S->当前单词.type != expected) {
      编译_错误(词法分析器S, 错误信息);
   }
}

//由于sourceCode未必来自于文件file,有可能只是个字符串,
//file仅用作跟踪待编译的代码的标识,方便报错
void 初始化词法分析器(虚拟机* 虚拟机S, 词法分析器* 词法分析器S, const char* 源码文件名, const char* 源码字符串, 模块对象S* objModule) {
   词法分析器S->源码文件名 = 源码文件名;
   词法分析器S->源码字符串 = 源码字符串;
   词法分析器S->当前字符 = *词法分析器S->源码字符串;
   词法分析器S->源码字符串的下一个字符 = 词法分析器S->源码字符串 + 1;
   词法分析器S->当前单词.行号 = 1;
   词法分析器S->当前单词.type = TOKEN_UNKNOWN;
   词法分析器S->当前单词.start = NULL;
   词法分析器S->当前单词.长度 = 0;
   词法分析器S->前一个单词 = 词法分析器S->当前单词;
   词法分析器S->应该插入的右括号数 = 0;
   词法分析器S->虚拟机S = 虚拟机S;
   词法分析器S->当前正在编译的模块 = objModule;
}
